package com.vast.ui.component.socketinterface.serverblocking;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.Executor;

import org.eclipse.core.runtime.ListenerList;

import com.vast.ui.component.socketinterface.listner.IReceiveListener;
import com.vast.ui.component.socketinterface.listner.ISocketConnectionListener;
import com.vast.ui.component.socketinterface.protocol.VaSTUIComponentProtocol;
import com.vast.ui.component.socketinterface.request.Request;
import com.vast.ui.component.socketinterface.request.RequestQueue;
import com.vast.ui.component.socketinterface.servercommon.IServer;
import com.vast.ui.component.state.Sequence;
import com.vast.ui.component.util.ConsoleLoggerSingleton;
import com.vast.ui.component.util.Logger;

public class BlockingServer implements IServer {
	private final      Sequence  sequence;
	public  int                  serverRecvPort;
	public  int                  serverSendPort;
	private final  ListenerList  receiveListenerList;
	private final  ListenerList  socketConnectionListenerList;
	private ServerSocket         serverRecvSocket;
	private ServerSocket         serverSendSocket;
	private Socket               clientRecvSocket;
	private Socket               clientSendSocket;
	private RequestQueue         sendQueue;
	private DataInputStream      in;
	private BufferedOutputStream out;
	private final Executor       executor;
	private final Logger         logger;
	
	public BlockingServer (int serverPort, Sequence sequence, RequestQueue sendQueue, Executor executor) {
		this.serverRecvPort   = serverPort;
		this.serverSendPort   = serverPort + 1;
		this.sequence         = sequence;
		this.sendQueue        = sendQueue;
		this.executor         = executor;
		this.logger           = ConsoleLoggerSingleton.getInstance();
		this.receiveListenerList = new ListenerList();
		this.socketConnectionListenerList = new ListenerList();
		this.clientRecvSocket = null;
		this.clientSendSocket = null;
		
	}
	
	public void addSocketConnectionListener(ISocketConnectionListener listener){
		socketConnectionListenerList.add(listener);
	}
	
	public void removeSocketConnectionListener(ISocketConnectionListener listener){
		socketConnectionListenerList.remove(listener);
	}
	
	public void addReceiveListener(IReceiveListener listener){
		receiveListenerList.add(listener);
	}
	
	public void removeReceiveListener(IReceiveListener listener){
		receiveListenerList.remove(listener);
	}
	
	public void serverRecvThread() {
		logger.writeEntry("Start serverRecvThread()");
		try {
			logger.writeEntry("Waiting for client Connection (port :" + serverRecvPort + ")");
			clientRecvSocket = serverRecvSocket.accept();
			
			in = new DataInputStream(new BufferedInputStream(clientRecvSocket.getInputStream()));
			while(sequence.isReceiveConnection()) {
				Short sizeOfData = in.readShort();
				Request request  = new Request(sizeOfData);
				request.writeShort(sizeOfData);
				in.read(request.data, 2, sizeOfData - 2);
				
				Short command = (short) ((request.data[2] << 8) + (request.data[3] << 0));
				if (command == VaSTUIComponentProtocol.PK_SOCKET_TERMINATE) {
					closeConnection();
					Object[] listeners = socketConnectionListenerList.getListeners();
					for (Object listener : listeners) {
						((ISocketConnectionListener) listener).notifyStateChange(sequence);
					};
					return;
				}
				Object[] listeners = receiveListenerList.getListeners();
				for (Object listener : listeners) {
					((IReceiveListener) listener).received(request);
				}
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			//closeConnection();
		}
	}
	
	public void serverSendThread() {
		logger.writeEntry("Start serverSendThread()");
		try {
			logger.writeEntry("Waiting for client Connection (port :" + serverSendPort + ")");
			clientSendSocket = serverSendSocket.accept();
					
			out = new BufferedOutputStream(clientSendSocket.getOutputStream());
			while(sequence.isSendConnection()) {
				Request request  = sendQueue.getRequest();
				out.write(request.data);
				out.flush();
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public void openConnection() {
		// TODO Auto-generated method stub		
		
		if(!sequence.isReceiveConnection()) {
			try {
				serverRecvSocket = new ServerSocket(serverRecvPort);
				executor.execute(
						new Runnable() {
							public void run() {
								serverRecvThread();
							}
						}
					);
				sequence.openReceiveConnection();
				logger.writeEntry("Open Receive Connection");
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		if(!sequence.isSendConnection()) {
			try {
				serverSendSocket = new ServerSocket(serverSendPort);
				executor.execute(
					new Runnable() {
						public void run() {
							serverSendThread();
						}
					}
				);
				sequence.openSendConnection();
				logger.writeEntry("Open Send Connection");
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
	public void closeConnection() {
		try {		
			if(clientRecvSocket != null) {
				clientRecvSocket.close();
				this.clientRecvSocket = null;
				
				logger.writeEntry("Close Client of Receive Connection");
			} else {
				logger.writeEntry("ClientSocket for Recv is not bind");
			}

			if(clientSendSocket != null) {
				clientSendSocket.close();
				this.clientSendSocket = null;
				logger.writeEntry("Close Client of Send Connection");
			} else {
				logger.writeEntry("ClientSocket for send is not bind");
			}
			
			serverRecvSocket.close();
			sequence.closeReceiveConnection();
			logger.writeEntry("Close Server of Receive Connection");
			
			serverSendSocket.close();
			sequence.closeSendConnection();
			logger.writeEntry("Close Server of Send Connection");	
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}
}
